# -*- Mode: Python -*-

import sys, os

target = os.environ.get('NRE_TARGET') or 'x86_64'
if target == 'x86_32':
    cross = 'i686-pc-nulnova'
elif target == 'x86_64':
    cross = 'x86_64-pc-nulnova'
else:
    print "Please define NRE_TARGET to x86_32 or x86_64 first!"
    Exit(1)
crossver = '4.8.1'
crossdir = os.path.abspath('/opt/nre-cross-' + target)

hostenv = Environment(
    ENV = os.environ,
    CXXFLAGS = '-Wall -Wextra -ansi -g'
)
env = Environment(
    CXXFLAGS = '-Wall -Wextra -ansi -std=c++0x -msse2',
    CFLAGS = '-Wall -Wextra -std=c99  -msse2',
    # without "-z max-page-size=0x1000" ld produces really large binaries for x86_64, because it
    # aligns to 2MiB (not only in virtual memory, but also in the binary)
    LINKFLAGS = '-nostartfiles -Wl,--no-undefined -static -Wl,-static -static-libgcc'
        + ' -Wl,-z,max-page-size=0x1000',
    ENV = {
        'PATH' : crossdir + "/bin:" + os.environ['PATH'],
        # required for colored outputs
        'HOME' : os.environ['HOME'],
        'TERM' : os.environ['TERM']
   	},
    CPPPATH = '#include',
    LD = cross + '-ld',
    AS = cross + '-as',
    AR = cross + '-ar',
    RANLIB = cross + '-ranlib',
)

compiler = os.environ.get('NRE_CC') or 'gcc'
if compiler == 'gcc':
    env.Replace(
        CXX = cross + '-g++',
        CC = cross + '-gcc'
    )
else:
    env.Replace(
        CXX = 'clang++',
        CC = 'clang',
        LINK = cross + '-gcc',
    )
    if target == 'x86_64':
        hosttriple = 'x86_64-pc-linux-gnu'
    else:
        hosttriple = 'i686-pc-linux-gnu'
    env.Append(
    	# clang causes alignment problems for SSE instructions. perhaps this is triggered by dlmalloc,
    	# i.e. c code? at least, it seems to happen only in mmap, which is only used by dlmalloc.
    	# clang doesn't support the function attribute force_align_arg_pointer, which would perhaps
    	# be enough. thus, we use -mstackrealign to align the stack in all functions as a workaround
    	# for now.
        CXXFLAGS = ' -mstackrealign -target ' + hosttriple,
        CFLAGS = ' -mstackrealign -target ' +  hosttriple,
        ASPPFLAGS = ' -Wa,-triple,' + hosttriple
    )

btype = os.environ.get('NRE_BUILD')
if btype == 'debug':
    env.Append(CXXFLAGS = ' -O0 -ggdb')
    env.Append(CFLAGS = ' -O0 -ggdb')
else:
    # we enable the framepointer to get stacktraces in release-mode. of course, we could also
    # disable stacktraces later, so that we don't need the framepointer.
    env.Append(CXXFLAGS = ' -O3 -DNDEBUG -fno-omit-frame-pointer')
    env.Append(CFLAGS = ' -O3 -DNDEBUG -fno-omit-frame-pointer')
    btype = 'release'
if target == 'x86_64':
    env.Append(CXXFLAGS = ' -mcmodel=large')
    env.Append(CFLAGS = ' -mcmodel=large')
    env.Append(LINKFLAGS = ' -mcmodel=large')
builddir = 'build/' + target + '-' + btype

guestenv = Environment(
    BINARYDIR = '#' + builddir + '/bin/apps',
    ASFLAGS = '--32',
    CFLAGS = '-Wall -Wextra -ansi -m32 -g -O0',
    CXXFLAGS = '-Wall -Wextra -ansi -m32 -fno-exceptions -fno-rtti -g -O0',
    LINKFLAGS = '-m32 -fno-hosted -nostdlib -Wall -Wextra -ansi -Wl,--build-id=none'
)

verbose = os.environ.get('NRE_VERBOSE',0);
if int(verbose) == 0:
    hostenv['ASCOMSTR'] = guestenv['ASCOMSTR'] = env['ASCOMSTR'] = "AS $TARGET"
    hostenv['ASPPCOMSTR'] = guestenv['ASPPCOMSTR'] = env['ASPPCOMSTR'] = "ASPP $TARGET"
    hostenv['CCCOMSTR'] = guestenv['CCCOMSTR'] = env['CCCOMSTR'] = "CC $TARGET"
    hostenv['CXXCOMSTR'] = guestenv['CXXCOMSTR'] = env['CXXCOMSTR'] = "CXX $TARGET"
    hostenv['LINKCOMSTR'] = guestenv['LINKCOMSTR'] = env['LINKCOMSTR'] = "LD $TARGET"
    hostenv['ARCOMSTR'] = guestenv['ARCOMSTR'] = env['ARCOMSTR'] = "AR $TARGET"
    hostenv['RANLIBCOMSTR'] = guestenv['RANLIBCOMSTR'] = env['RANLIBCOMSTR'] = "RANLIB $TARGET"

env.Append(
    ARCH = target,
    ROOTDIR = '#',
    BUILDDIR = '#' + builddir,
    BINARYDIR = '#' + builddir + '/bin/apps',
    LIBPATH = '#' + builddir + '/bin/lib',
    SYSLIBPATH = crossdir + '/lib',
    CROSS = cross
)
if target == 'x86_64':
    env.Append(LINK_ADDR = 0x300000000)
else:
    env.Append(LINK_ADDR = 0x100000)

def NREProgram(env, target, source, libs = [], cpppath = [], fixedaddr = False, ldscript = 'default'):
    # for better debugging: put every executable at a different address
    myenv = env.Clone()
    myenv.Replace(CPPPATH = [myenv['CPPPATH']] + cpppath)
    if not fixedaddr:
        myenv.Append(LINKFLAGS = ' -Wl,--section-start=.text=' + ("0x%x" % env['LINK_ADDR']))
        env['LINK_ADDR'] += 0x100000
    myenv['LINKFLAGS'] += ' -Wl,-T,toolchain/' + ldscript + '.ld'
    prog = myenv.Program(
        target, [myenv['LIBPATH'] + '/crt0.o'] + source,
        LIBS = ['stdc++','supc++'] + libs,
        LIBPATH = [myenv['LIBPATH'], myenv['SYSLIBPATH']]
    )
    myenv.Depends(prog, myenv['LIBPATH'] + '/crt0.o')
    for l in ['stdc++', 'c', 'm', 'g'] + libs:
        myenv.Depends(prog, myenv['LIBPATH'] + '/lib' + l + '.a')
    myenv.Depends(prog, '#toolchain/' + ldscript + '.ld')
    myenv.Install(env['BINARYDIR'], prog)
    return prog

env.NREProgram = NREProgram
hostenv.SConscript('tools/SConscript', 'hostenv',
                   variant_dir = builddir + '/tools', duplicate = 0)

for d in ['libs', 'services', 'apps', 'dist']:
    env.SConscript(d + '/SConscript', 'env',
                   variant_dir = builddir + '/' + d, duplicate = 0)

env.SConscript('guests/SConscript', 'guestenv',
               variant_dir = builddir + '/guests', duplicate = 0)

# EOF
